/**
 * ExponentialDistribution : Definition de la loi exponentielle
 *
 * @author 
 * @version 0.1, 04/07/02 
 */
package br.unb.statistic.distribution;  // Permet de specifier que cette classe appartient au package Statistique

import java.util.HashMap;

import br.unb.statistic.InterPacketTimeGenerator;
import br.unb.statistic.RandomGenerator;
import br.unb.statistic.inference.Inference;
import br.unb.utils.Resource;

public class ExponentialDistribution extends GenericContinuousDistribution implements RandomGenerator, InterPacketTimeGenerator {

    /**
     * Equation de la densite: 
     *    f(x)= 1/lambda * exp(-x/lambda) pour x>0, lambda>0
     *    where 1/lambda = mean time of a process 
     */
    public static int LAMBDA = 0;

    //Definition des constructeurs
    public ExponentialDistribution() {
        paramsCount = 1;
        name = new String("Exponential");

        params = new double[paramsCount]; // initialisation du tableau des valeurs des parametres
        paramNames = new String[paramsCount]; // initialisation du tableau des noms des parametres
        params[LAMBDA] = 0d;  //0.1d normalement debug 17/01/0.
        paramNames[LAMBDA] = new String("lambda");
    }

    public ExponentialDistribution(double unLambda) throws IllegalArgumentException {
        this();
        if (unLambda > 0) {
            setParam(LAMBDA, unLambda);
        } else {
            throw new IllegalArgumentException(Resource.getString("EXPONENTIALDISTRIBUTION.CONSTRUCTOR_LAMBDA_<_0"));
        }
    }

    // Ce constructeur n'a pas d'interet sauf celui d'etre compatible avec les autres distributions dans un cas general
    public ExponentialDistribution(double[] desParams) throws IllegalArgumentException {
        this();
        if (hasValidParam(desParams)) {
            setParams(desParams);
        } else {
            throw new IllegalArgumentException(Resource.getString("EXPONENTIALDISTRIBUTION.CONSTRUCTOR_LAMBDA_<_0"));
        }
    }

    public boolean hasValidParam(double[] desParams) {
        if (desParams[LAMBDA] > 0) {
            return true;
        } else {
            return false;
        }
    }

    // Definitions de quelques methodes
    /**
     * Returns the value of the probability distribution function at x
     */
    public double pdf(double x) {
        if (x >= 0) {
            return (params[LAMBDA] / Math.exp(params[LAMBDA] * x));
        } else {
            return 0;
        }
    }

    /**
     * Returns the value of the cumulative distribution function at x (=Fonction de repartition)
     */
    public double cdf(double x) {
        if (x > 0) {
            return (1.0d - Math.exp(-1.0d * params[LAMBDA] * x));
        } else {
            return 0;
        }
    }

    /**
     * Return the value of Inverse Function at x 
     * @param x with x>=0 and x<=1
     */
    public double inverseCdf(double x) throws IllegalArgumentException {
        if (x >= 0 && x <= 1) {
            return -1d / params[LAMBDA] * Math.log(1d - x);
        } else {
            throw new IllegalArgumentException(Resource.getString("EXPONENTIALDISTRIBUTION.INVERSECDF_X_MUST_BE_>_0_AND_<_1"));
        }
    }

    public double rand() {
        return inverseCdf(random.nextDouble());
    }

    public double mean() {
        return (1d / params[LAMBDA]);
    }

    public double var() {
        return Math.pow(1.0d / params[LAMBDA], 2.0d);
    }

    public double nextInterPacketTime() {
        return rand();
    }

    public String getEquation() {
        return new String(" f(x)= 1/lambda * exp(-x/lambda) pour x>0, lambda>0 where 1/lambda = mean time of a process");
    }

    public void setParameters(HashMap parameters) {
        double lambda;
        if (parameters.get("mean") != null) {
            lambda = 1 / java.lang.Double.parseDouble((String) parameters.get("mean"));
        } else {
            lambda = java.lang.Double.parseDouble((String) parameters.get("rate"));
        }
        if (lambda > 0) {
            setParam(LAMBDA, lambda);
        }
    }

    public void estimateParameters(Inference inf) {
        setParam(LAMBDA, (1 / inf.getMean()));
    }
    /* //Old code with the testing unit and stuff YO!!!
    public static void main(String args[]){
    ExponentialDistribution dist = new ExponentialDistribution(3.5);
    ExponentialDistribution dist2 = new ExponentialDistribution();
    Log log = new Log("teste",false);
    for(int i=0; i<3000; i++){
    log.write(""+dist.rand());
    }
    log.close();
    try {
    DataReader dr = new DataReader(log.getFile());
    Inference inf = new Inference(dr,0);
    dist2.calculateMLE(inf);
    System.out.println(dist2.getParam(0));
    } catch (IOException e) {
    e.printStackTrace();
    }
    }*/
}
